#include <aphw/Cursor.hpp>

#include <boost/python.hpp>
#include <ham/hamsterdb.h>

#include <aphw/Database.hpp>
#include <aphw/Exceptions.hpp>

#include <iostream>

namespace
{

enum MoveFlags
{
    FIRST = HAM_CURSOR_FIRST,
    LAST = HAM_CURSOR_LAST,
    NEXT = HAM_CURSOR_NEXT,
    PREVIOUS = HAM_CURSOR_PREVIOUS
};



ham_status_t move(aphw::Cursor& c,
                  MoveFlags dir)
{
    return ham_cursor_move(c.cur(),
                           c.key(),
                           c.rec(),
                           dir);
}

}

namespace aphw
{

struct Cursor::Body
{
    Body(aphw::Database& db) :
        cur_ (NULL),
        open_ (true)
        {
            ham_status_t err =
                ham_cursor_create(db.db(),
                                  *db.txn(),
                                  0,
                                  &cur_);
            if (HAM_OUT_OF_MEMORY == err)
                throw OutOfMemoryError();
    
            assert(cur_);
        }
    
    ~Body()
        {
            assert(cur_);
            close();
        }
        
    void close() {
        if (open_) {
            ham_cursor_close(cur_);
            open_ = false;
        }
    }

    ham_cursor_t* cur_;
    bool open_;
    Key key_;
    GetRecord rec_;
};

Cursor::Cursor(Database& db) :
    body_ (new Body(db))
{}

bool Cursor::open() const {
    return body_->open_;
}

void Cursor::close() {
    assert(body_);
    body_->close();
}

ham_cursor_t* Cursor::cur()
{
    assert(body_);
    assert(body_->cur_);
    
    return body_->cur_;
}

ham_key_t* Cursor::key()
{
    assert(getKey().key());
    
    return body_->key_.key();
}

ham_record_t* Cursor::rec()
{
    assert(getRecord().rec());
    
    return body_->rec_.rec();
}

Key Cursor::getKey()
{
    assert(body_);
    return body_->key_;
}

GetRecord Cursor::getRecord()
{
    assert(body_);
    return body_->rec_;
}

void init_Cursor()
{
    using namespace boost::python;

    class_<Cursor>("Cursor", init<Database&>())
        .add_property("key", &Cursor::getKey)
        .add_property("record", &Cursor::getRecord)
        .add_property("open", &Cursor::open)
        .def("close", &Cursor::close)
        ;

    enum_< ::MoveFlags >("CursorMove")
        .value("FIRST", ::FIRST)
        .value("LAST", ::LAST)
        .value("NEXT", ::NEXT)
        .value("PREVIOUS", PREVIOUS)
        ;

    def("move_cursor", ::move);
}

}
